﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Text;
using System.Reflection;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Infrastructure.Aspect.ExceptionHandling;
namespace Infrastructure.Aspect.Inspectors
{

    public interface IMethodAuditService
    {
        MethodAudit GetOne(Guid Id);
        void RegisterMethodAudit(MethodBase info, Guid operation);
        void AddMethodAudit(MethodAudit audit);        
        void UpdateMethodAudit(MethodAudit audit);
        void DeleteMethodAudit(MethodAudit audit);
        List<MethodAudit> GetAll();
        double GetTimeAverage(string Solution, string module, string classname, string method);
    }

    public class MethodAuditService : IMethodAuditService
    {
        private SqlConnection GetSqlConnection()
        {
            var strcon = ConfigurationManager.ConnectionStrings[Constants.ConnectionStringNames.InfrastructureConnectionString].ConnectionString;
            return new SqlConnection(strcon);
        }
        private void LogException(SqlException sqlex)
        {
            EventLog el = new EventLog();
            el.Source = "CustomAppLog";
            string strMessage;
            strMessage = "Exception Number : " + sqlex.Number +
                         "(" + sqlex.Message + ") has occurred";
            el.WriteEntry(strMessage);
            foreach (SqlError sqle in sqlex.Errors)
            {
                strMessage = "Message: " + sqle.Message +
                             " Number: " + sqle.Number +
                             " Procedure: " + sqle.Procedure +
                             " Server: " + sqle.Server +
                             " Source: " + sqle.Source +
                             " State: " + sqle.State +
                             " Severity: " + sqle.Class +
                             " LineNumber: " + sqle.LineNumber;
                el.WriteEntry(strMessage);
            }
        }
        private MethodAudit BuildAudit(SqlDataReader reader)
        {

            int idindex = reader.GetOrdinal("Id");
            int solutionIndex = reader.GetOrdinal("Solution");
            int methodIndex = reader.GetOrdinal("Method");
            int moduleIndex = reader.GetOrdinal("Module");
            int classNameIndex = reader.GetOrdinal("ClassName");
            int createdIndex = reader.GetOrdinal("Created");
            int operationIndex = reader.GetOrdinal("Operation");

            var item = new MethodAudit();
            item.Id = reader.GetGuid(idindex);
            item.Method = reader.GetString(methodIndex);
            item.ClassName = reader.GetString(classNameIndex);
            item.Module = reader.GetString(moduleIndex);
            item.Solution = reader.GetString(solutionIndex);
            item.Operation = reader.GetGuid(operationIndex);
            if (!reader.IsDBNull(createdIndex))
                item.Created = reader.GetDateTime(createdIndex);

            return item;
        }

        public List<MethodAudit> GetAll()
        {
            SqlConnection con = null;
            List<MethodAudit> result = new List<MethodAudit>();
            try
            {
                con = GetSqlConnection();
                var cmd = con.CreateCommand();
                cmd.CommandText = "usp_MethodAudit_getall";
                cmd.CommandType = CommandType.StoredProcedure;                
                con.Open();
                var reader = cmd.ExecuteReader(CommandBehavior.SingleResult);
                int idindex = reader.GetOrdinal("Id");
                int methodIndex = reader.GetOrdinal("Method");
                int moduleIndex = reader.GetOrdinal("Module");
                int classNameIndex = reader.GetOrdinal("ClassName");
                int createdIndex = reader.GetOrdinal("Created");
                while (reader.Read())
                {
                    var item = new MethodAudit();
                    item.Id = reader.GetGuid(idindex);
                    item.Method = reader.GetString(methodIndex);
                    item.ClassName = reader.GetString(classNameIndex);
                    item.Module = reader.GetString(moduleIndex);
                    if (!reader.IsDBNull(createdIndex))
                        item.Created = reader.GetDateTime(createdIndex);
                    result.Add(item);
                }
                reader.Close();
            }
            catch (SqlException ex)
            {
                LogException(ex);
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, ExceptionPolicyNames.InfrastructurePolicy);
                if (rethrow)
                    throw;
            }
            finally
            {
                if (con != null) con.Close();
            }
            return result;

        }
        public MethodAudit GetOne(Guid Id)
        {
            SqlConnection con = null;
            MethodAudit result = null;
            try
            {
                con = GetSqlConnection();
                var cmd = con.CreateCommand();
                cmd.CommandText = "usp_MethodAudit_getone";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@Id", Id);                
                con.Open();
                var reader =  cmd.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.SingleRow);
                int idindex = reader.GetOrdinal("Id");
                int methodIndex = reader.GetOrdinal("Method");
                int moduleIndex = reader.GetOrdinal("Module");
                int createdIndex = reader.GetOrdinal("Created");
                int classNameIndex = reader.GetOrdinal("ClassName");            
                while (reader.Read())
                {
                    result = new MethodAudit();
                    result.Id = reader.GetGuid(idindex);
                    result.Method = reader.GetString(methodIndex);
                    result.ClassName = reader.GetString(classNameIndex);
                    result.Module = reader.GetString(moduleIndex);
                    if (!reader.IsDBNull(createdIndex))
                        result.Created = reader.GetDateTime(createdIndex);
                }
                reader.Close();
            }
            catch (SqlException ex)
            {
                LogException(ex);
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, ExceptionPolicyNames.InfrastructurePolicy);
                if (rethrow)
                    throw;
            }
            finally
            {
                if (con != null) con.Close();
            }
            return result;
        }

        //RAISERROR( 'Unknown Product ID: %s', 16, 1, @ProductID )
        //sp_addmessage
        public void RegisterMethodAudit(MethodBase info, Guid operation)
        {
            
            var audit = new MethodAudit(); 
            audit.Id = Guid.NewGuid(); 
            audit.Method = info.Name;
            audit.ClassName = info.DeclaringType.FullName;
            audit.Module =  info.DeclaringType.Assembly.FullName;
            audit.Created = DateTime.Now;
            audit.Application = Guid.NewGuid();
            audit.Solution = AppDomain.CurrentDomain.FriendlyName;
            audit.Operation = operation;
            this.AddMethodAudit(audit);
        }
        public void AddMethodAudit(MethodAudit audit)
        {

            SqlConnection con = null;
            try
            {
                con = GetSqlConnection();
                var cmd = con.CreateCommand();
                cmd.CommandText = "usp_MethodAudit_insert";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@Id", audit.Id);                
                cmd.Parameters.AddWithValue("@Module", audit.Module);
                cmd.Parameters.AddWithValue("@ClassName", audit.ClassName);
                cmd.Parameters.AddWithValue("@Method", audit.Method);
                cmd.Parameters.AddWithValue("@Created", audit.Created);
                cmd.Parameters.AddWithValue("@Application", audit.Application);
                cmd.Parameters.AddWithValue("@Solution", audit.Solution);
                cmd.Parameters.AddWithValue("@Operation", audit.Operation);  
                con.Open();
                cmd.ExecuteNonQuery();
            }
            catch (SqlException ex)
            {
                LogException(ex);
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, ExceptionPolicyNames.InfrastructurePolicy);
                if (rethrow)
                    throw;
            }
            finally
            {
                if (con != null) con.Close();
            }
        }
        public void UpdateMethodAudit(MethodAudit audit)
        {
            SqlConnection con = null;
            try
            {
                con = GetSqlConnection();
                var cmd = con.CreateCommand();
                cmd.CommandText = "usp_MethodAudit_update";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@Id", audit.Id);
                cmd.Parameters.AddWithValue("@Method", audit.Method);
                con.Open();
                cmd.ExecuteNonQuery();
            }
            catch (SqlException ex)
            {
                LogException(ex);
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, ExceptionPolicyNames.InfrastructurePolicy);
                if (rethrow)
                    throw;
            }
            finally
            {
                if (con != null) con.Close();
            }
        }
        public void DeleteMethodAudit(MethodAudit audit)
        {
            SqlConnection con = null;
            try
            {
                con = GetSqlConnection();
                var cmd = con.CreateCommand();
                cmd.CommandText = "usp_MethodAudit_delete";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@Id", audit.Id);                
                con.Open();
                cmd.ExecuteNonQuery();
            }
            catch (SqlException ex)
            {
                LogException(ex);
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, ExceptionPolicyNames.InfrastructurePolicy);
                if (rethrow)
                    throw;
            }
            finally
            {
                if (con != null) con.Close();
            }
        }
        public double GetTimeAverage(string Solution, string module, string classname, string method)
        {
            SqlConnection con = null;
            var audits = new List<MethodAudit>();
            double result = 0;
            try
            {
                con = GetSqlConnection();
                var cmd = con.CreateCommand();
                cmd.CommandText = "usp_MethodAudit_getbySolutionModuleClassNameMethod";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@Solution", Solution);
                cmd.Parameters.AddWithValue("@Module", module);
                cmd.Parameters.AddWithValue("@ClassName", classname);
                cmd.Parameters.AddWithValue("@Method", method);
                con.Open();
                var reader = cmd.ExecuteReader(CommandBehavior.SingleResult);                
                while (reader.Read())
                {
                    var item = this.BuildAudit(reader);
                    audits.Add(item);
                }
                reader.Close();
            }
            catch (SqlException ex)
            {
                LogException(ex);
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, ExceptionPolicyNames.InfrastructurePolicy);
                if (rethrow)
                    throw;
            }
            finally
            {
                if (con != null) con.Close();
            }

            var groups = from c in audits group c by c.Operation into g select new { id = g.Key, list = g.OrderBy(a=>a.Created) };

            foreach (var g in groups)
            {
                var ini = g.list.FirstOrDefault();
                var last = g.list.LastOrDefault();
                result += last.Created.Value.Subtract(ini.Created.Value).TotalMilliseconds;                                
            }
            return result;
        }

    }
}
